home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 051-075 / disk_068 / mg1b / search.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  14KB  |  644 lines

  1. /*
  2.  *         Search commands.
  3.  * The functions in this file implement the
  4.  * search commands (both plain and incremental searches
  5.  * are supported) and the query-replace command.
  6.  *
  7.  * The plain old search code is part of the original
  8.  * MicroEMACS "distribution". The incremental search code,
  9.  * and the query-replace code, is by Rich Ellison.
  10.  */
  11. #include    "def.h"
  12.  
  13. #define SRCH_BEGIN    (0)            /* Search sub-codes.    */
  14. #define    SRCH_FORW    (-1)
  15. #define SRCH_BACK    (-2)
  16. #define SRCH_NOPR    (-3)
  17. #define SRCH_ACCM    (-4)
  18. #define    SRCH_MARK    (-5)
  19.  
  20. typedef struct  {
  21.     int    s_code;
  22.     LINE    *s_dotp;
  23.     int    s_doto;
  24. }    SRCHCOM;
  25.  
  26. static    SRCHCOM    cmds[NSRCH];
  27. static    int    cip;
  28.  
  29. int    srch_lastdir = SRCH_NOPR;        /* Last search flags.    */
  30.  
  31. VOID    is_cpush();
  32. VOID    is_lpush();
  33. VOID    is_pop();
  34. VOID    is_prompt();
  35. VOID    is_dspl();
  36.  
  37. /*
  38.  * Search forward.
  39.  * Get a search string from the user, and search for it,
  40.  * starting at ".". If found, "." gets moved to just after the
  41.  * matched characters, and display does all the hard stuff.
  42.  * If not found, it just prints a message.
  43.  */
  44. /*ARGSUSED*/
  45. forwsearch(f, n, k) {
  46.     register int    s;
  47.  
  48.     if ((s=readpattern("Search")) != TRUE)
  49.         return (s);
  50.     if (forwsrch() == FALSE) {
  51.         ewprintf("Search failed: \"%s\"", pat);
  52.         return (FALSE);
  53.     }
  54.     srch_lastdir = SRCH_FORW;
  55.     return (TRUE);
  56. }
  57.  
  58. /*
  59.  * Reverse search.
  60.  * Get a search string from the  user, and search, starting at "."
  61.  * and proceeding toward the front of the buffer. If found "." is left
  62.  * pointing at the first character of the pattern [the last character that
  63.  * was matched].
  64.  */
  65. /*ARGSUSED*/
  66. backsearch(f, n, k) {
  67.     register int    s;
  68.  
  69.     if ((s=readpattern("Search backward")) != TRUE)
  70.         return (s);
  71.     if (backsrch() == FALSE) {
  72.         ewprintf("Search failed: \"%s\"", pat);
  73.         return (FALSE);
  74.     }
  75.     srch_lastdir = SRCH_BACK;
  76.     return (TRUE);
  77. }
  78.  
  79. /* 
  80.  * Search again, using the same search string
  81.  * and direction as the last search command. The direction
  82.  * has been saved in "srch_lastdir", so you know which way
  83.  * to go.
  84.  */
  85. /*ARGSUSED*/
  86. searchagain(f, n, k) {
  87.     if (srch_lastdir == SRCH_FORW) {
  88.         if (forwsrch() == FALSE) {
  89.             ewprintf("Search failed: \"%s\"", pat);
  90.             return (FALSE);
  91.         }
  92.         return (TRUE);
  93.     }
  94.     if (srch_lastdir == SRCH_BACK) {
  95.         if (backsrch() == FALSE) {
  96.             ewprintf("Search failed: \"%s\"", pat);
  97.             return (FALSE);
  98.         }
  99.         return (TRUE);
  100.     }
  101.     ewprintf("No last search");
  102.     return (FALSE);
  103. }
  104.  
  105. /*
  106.  * Use incremental searching, initially in the forward direction.
  107.  * isearch ignores any explicit arguments.
  108.  */
  109. /*ARGSUSED*/
  110. forwisearch(f, n, k) {
  111.     return (isearch(SRCH_FORW));
  112. }
  113.  
  114. /*
  115.  * Use incremental searching, initially in the reverse direction.
  116.  * isearch ignores any explicit arguments.
  117.  */
  118. /*ARGSUSED*/
  119. backisearch(f, n, k) {
  120.     return (isearch(SRCH_BACK));
  121. }
  122.  
  123. /*
  124.  * Incremental Search.
  125.  *    dir is used as the initial direction to search.
  126.  *    ^S    switch direction to forward
  127.  *    ^R    switch direction to reverse
  128.  *    ^Q    quote next character (allows searching for ^N etc.)
  129.  *    <ESC>    exit from Isearch
  130.  *    <DEL>    undoes last character typed. (tricky job to do this correctly).
  131.  *    other ^    exit search, don't set mark
  132.  *    else    accumulate into search string
  133.  */
  134. isearch(dir) {
  135.     register int    c;
  136.     register LINE    *clp;
  137.     register int    cbo;
  138.     register int    success;
  139.     int        pptr;
  140.     char        opat[NPAT];
  141.  
  142.     for (cip=0; cip<NSRCH; cip++)
  143.         cmds[cip].s_code = SRCH_NOPR;
  144.     (VOID) strcpy(opat, pat);
  145.     cip = 0;
  146.     pptr = -1;
  147.     clp = curwp->w_dotp;
  148.     cbo = curwp->w_doto;
  149.     is_lpush();
  150.     is_cpush(SRCH_BEGIN);
  151.     success = TRUE;
  152.     is_prompt(dir, TRUE, success);
  153.     for (;;) {
  154.         update();
  155.         switch (c = (char) getkey(KQUOTE)) {
  156.         case METACH:
  157.             srch_lastdir = dir;
  158.             curwp->w_markp = clp;
  159.             curwp->w_marko = cbo;
  160.             if (kbdmop == NULL) ewprintf("Mark set");
  161.             return (TRUE);
  162.  
  163.         case CCHR('G'):
  164.             if (success != TRUE) {
  165.                 while (is_peek() == SRCH_ACCM)
  166.                     if (is_undo(&pptr, &dir) == FALSE)
  167.                         break;
  168.                 success = TRUE;
  169.                 is_prompt(dir, pptr < 0, success);
  170.                 break;
  171.             }
  172.             curwp->w_dotp = clp;
  173.             curwp->w_doto = cbo;
  174.             curwp->w_flag |= WFMOVE;
  175.             srch_lastdir = dir;
  176.             (VOID) ctrlg(FALSE, 0, KRANDOM);
  177.             (VOID) strcpy(pat, opat);
  178.             return ABORT;
  179.  
  180.         case CCHR('S'):
  181.             if (dir == SRCH_BACK) {
  182.                 dir = SRCH_FORW;
  183.                 is_lpush();
  184.                 is_cpush(SRCH_FORW);
  185.                 success = TRUE;
  186.             }
  187.             if (success==FALSE && dir==SRCH_FORW)
  188.                 break;
  189.             is_lpush();
  190.             pptr = strlen(pat);
  191.             (VOID) forwchar(FALSE, 1, KRANDOM);
  192.             if (is_find(SRCH_FORW) != FALSE) is_cpush(SRCH_MARK);
  193.             else {
  194.                 (VOID) backchar(FALSE, 1, KRANDOM);
  195.                 ttbeep();
  196.                 success = FALSE;
  197.             }
  198.             is_prompt(dir, pptr < 0, success);
  199.             break;
  200.  
  201.         case CCHR('R'):
  202.             if (dir == SRCH_FORW) {
  203.                 dir = SRCH_BACK;
  204.                 is_lpush();
  205.                 is_cpush(SRCH_BACK);
  206.                 success = TRUE;
  207.             }
  208.             if (success==FALSE && dir==SRCH_BACK)
  209.                 break;
  210.             is_lpush();
  211.             pptr = strlen(pat);
  212.             (VOID) backchar(FALSE, 1, KRANDOM);
  213.             if (is_find(SRCH_BACK) != FALSE) is_cpush(SRCH_MARK);
  214.             else {
  215.                 (VOID) forwchar(FALSE, 1, KRANDOM);
  216.                 ttbeep();
  217.                 success = FALSE;
  218.             }
  219.             is_prompt(dir, pptr < 0, success);
  220.             break;
  221.  
  222.         case 0x7F:
  223.             if (is_undo(&pptr, &dir) != TRUE) return FALSE;
  224.             if (is_peek() != SRCH_ACCM) success = TRUE;
  225.             is_prompt(dir, pptr < 0, success);
  226.             break;
  227.  
  228.         case CCHR('Q'):
  229.             c = (char) getkey(KQUOTE);
  230.             goto  addchar;
  231.         case CCHR('M'):
  232.             c = CCHR('J');
  233.         case CCHR('J'):
  234.             goto  addchar;
  235.  
  236.         default:
  237.             if (ISCTRL(c) != FALSE) {
  238.                 c += '@';
  239.                 c |= KCTRL;
  240.                 success = execute((KEY) c, FALSE, 1);
  241.                 curwp->w_markp = clp;
  242.                 curwp->w_marko = cbo;
  243.                 if (kbdmop == NULL) ewprintf("Mark set");
  244.                 curwp->w_flag |= WFMOVE;
  245.                 return (success);
  246.             }                
  247.         addchar:
  248.             if (pptr == -1)
  249.                 pptr = 0;
  250.             if (pptr == 0)
  251.                 success = TRUE;
  252.             pat[pptr++] = c;
  253.             if (pptr == NPAT) {
  254.                 ewprintf("Pattern too long");
  255.                 return FALSE;
  256.             }
  257.             pat[pptr] = '\0';
  258.             is_lpush();
  259.             if (success != FALSE) {
  260.                 if (is_find(dir) != FALSE)
  261.                     is_cpush(c);
  262.                 else {
  263.                     success = FALSE;
  264.                     ttbeep();
  265.                     is_cpush(SRCH_ACCM);
  266.                 }
  267.             } else
  268.                 is_cpush(SRCH_ACCM);
  269.             is_prompt(dir, FALSE, success);
  270.         }
  271.     }
  272.     /*NOTREACHED*/
  273. }
  274.  
  275. VOID
  276. is_cpush(cmd) register int cmd; {
  277.     if (++cip >= NSRCH)
  278.         cip = 0;
  279.     cmds[cip].s_code = cmd;
  280. }
  281.  
  282. VOID
  283. is_lpush() {
  284.     register int    ctp;
  285.  
  286.     ctp = cip+1;
  287.     if (ctp >= NSRCH)
  288.         ctp = 0;
  289.     cmds[ctp].s_code = SRCH_NOPR;
  290.     cmds[ctp].s_doto = curwp->w_doto;
  291.     cmds[ctp].s_dotp = curwp->w_dotp;
  292. }
  293.  
  294. VOID
  295. is_pop() {
  296.     if (cmds[cip].s_code != SRCH_NOPR) {
  297.         curwp->w_doto  = cmds[cip].s_doto; 
  298.         curwp->w_dotp  = cmds[cip].s_dotp;
  299.         curwp->w_flag |= WFMOVE;
  300.         cmds[cip].s_code = SRCH_NOPR;
  301.     }
  302.     if (--cip <= 0)
  303.         cip = NSRCH-1;
  304. }
  305.  
  306. is_peek() {
  307.     return cmds[cip].s_code;
  308. }
  309.  
  310. is_undo(pptr, dir) register int *pptr; register int *dir; {
  311.     register int    redo = FALSE ;
  312.     switch (cmds[cip].s_code) {
  313.     case SRCH_BEGIN:
  314.     case SRCH_NOPR:
  315.         *pptr = -1;
  316.     case SRCH_MARK:
  317.         break;
  318.  
  319.     case SRCH_FORW:
  320.         *dir = SRCH_BACK;
  321.         redo = TRUE;
  322.         break;
  323.  
  324.     case SRCH_BACK:
  325.         *dir = SRCH_FORW;
  326.         redo = TRUE;
  327.         break;
  328.  
  329.     case SRCH_ACCM:
  330.     default:
  331.         *pptr -= 1;
  332.         if (*pptr < 0)
  333.             *pptr = 0;
  334.         pat[*pptr] = '\0';
  335.         break;
  336.     }
  337.     is_pop();
  338.     if (redo) return is_undo(pptr, dir);
  339.     return (TRUE);
  340. }
  341.  
  342. is_find(dir) register int dir; {
  343.     register int    plen, odoto;
  344.     register LINE    *odotp ;
  345.  
  346.     odoto = curwp->w_doto;
  347.     odotp = curwp->w_dotp;
  348.     plen = strlen(pat);
  349.     if (plen != 0) {
  350.         if (dir==SRCH_FORW) {
  351.             (VOID) backchar(TRUE, plen, KRANDOM);
  352.             if (forwsrch() == FALSE) {
  353.                 curwp->w_doto = odoto;
  354.                 curwp->w_dotp = odotp;
  355.                 return (FALSE);
  356.             }
  357.             return (TRUE);
  358.         }
  359.         if (dir==SRCH_BACK) {
  360.             (VOID) forwchar(TRUE, plen, KRANDOM);
  361.             if (backsrch() == FALSE) {
  362.                 curwp->w_doto = odoto;
  363.                 curwp->w_dotp = odotp;
  364.                 return (FALSE);
  365.             }
  366.             return (TRUE);
  367.         }
  368.         ewprintf("bad call to is_find");
  369.         return FALSE;
  370.     }
  371.     return (FALSE);
  372. }
  373.  
  374. /*
  375.  * If called with "dir" not one of SRCH_FORW
  376.  * or SRCH_BACK, this routine used to print an error
  377.  * message. It also used to return TRUE or FALSE,
  378.  * depending on if it liked the "dir". However, none
  379.  * of the callers looked at the status, so I just
  380.  * made the checking vanish.
  381.  */
  382. VOID
  383. is_prompt(dir, flag, success) {
  384.     VOID is_dspl();
  385.  
  386.     if (dir == SRCH_FORW) {
  387.         if (success != FALSE)
  388.             is_dspl("I-search", flag);
  389.         else
  390.             is_dspl("Failing I-search", flag);
  391.     } else if (dir == SRCH_BACK) {
  392.         if (success != FALSE)
  393.             is_dspl("I-search backward", flag);
  394.         else
  395.             is_dspl("Failing I-search backward", flag);
  396.     } else ewprintf("Broken call to is_prompt");
  397. }
  398.  
  399. /*
  400.  * Prompt writing routine for the incremental search. 
  401.  * The "prompt" is just a string. The "flag" determines
  402.  * whether pat should be printed.
  403.  */
  404. VOID
  405. is_dspl(prompt, flag) char *prompt; {
  406.  
  407.     if (kbdmop != NULL) return;
  408.     if (flag != FALSE)
  409.         ewprintf("%s: ", prompt);
  410.     else
  411.         ewprintf("%s: %s", prompt, pat);
  412. }
  413.  
  414. /*
  415.  * Query Replace.
  416.  *    Replace strings selectively.  Does a search and replace operation.
  417.  */
  418. /*ARGSUSED*/
  419. queryrepl(f, n, k) {
  420.     register int    s;
  421.     register int    rcnt = 0;    /* Replacements made so far    */
  422.     register int    plen;        /* length of found string    */
  423.     char        news[NPAT];    /* replacement string        */
  424.  
  425.     if ((s=readpattern("Query replace")) != TRUE)
  426.         return (s);
  427.     if ((s=ereply("Query replace %s with: ",news, NPAT, pat)) == ABORT)
  428.         return (s);
  429.     if (s == FALSE)
  430.         news[0] = '\0';
  431.     if (kbdmop == NULL) ewprintf("Query replacing %s with %s:", pat, news);
  432.     plen = strlen(pat);
  433.  
  434.     /*
  435.      * Search forward repeatedly, checking each time whether to insert
  436.      * or not.  The "!" case makes the check always true, so it gets put
  437.      * into a tighter loop for efficiency.
  438.      */
  439.  
  440.     while (forwsrch() == TRUE) {
  441.     retry:
  442.         update();
  443.         switch (getkey(KQUOTE)) {
  444.         case ' ':
  445.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  446.                 return (FALSE);
  447.             rcnt++;
  448.             break;
  449.  
  450.         case '.':
  451.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  452.                 return (FALSE);
  453.             rcnt++;
  454.             goto stopsearch;
  455.  
  456.         case CCHR('G'):    /* ^G or ESC */
  457.             (VOID) ctrlg(FALSE, 0, KRANDOM);
  458.         case 033:
  459.             goto stopsearch;
  460.  
  461.         case '!':
  462.             do {
  463.                 if (lreplace((RSIZE) plen, news, f) == FALSE)
  464.                     return (FALSE);
  465.                 rcnt++;
  466.             } while (forwsrch() == TRUE);
  467.             goto stopsearch;
  468.  
  469.         case 0x7F:        /* To not replace */
  470.             break;
  471.  
  472.         default:
  473. ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
  474.             goto retry;
  475.         }
  476.     }
  477. stopsearch:
  478.     curwp->w_flag |= WFHARD;
  479.     update();
  480.     if (kbdmop == NULL) {
  481.         if (rcnt == 0)
  482.             ewprintf("(No replacements done)");
  483.         else if (rcnt == 1)
  484.             ewprintf("(1 replacement done)");
  485.         else
  486.             ewprintf("(%d replacements done)", rcnt);
  487.     }
  488.     return TRUE;
  489. }
  490.  
  491. /*
  492.  * This routine does the real work of a
  493.  * forward search. The pattern is sitting in the external
  494.  * variable "pat". If found, dot is updated, the window system
  495.  * is notified of the change, and TRUE is returned. If the
  496.  * string isn't found, FALSE is returned.
  497.  */
  498. forwsrch() {
  499.     register LINE    *clp;
  500.     register int    cbo;
  501.     register LINE    *tlp;
  502.     register int    tbo;
  503.     register char    *pp;
  504.     register int    c;
  505.  
  506.     clp = curwp->w_dotp;
  507.     cbo = curwp->w_doto;
  508.     while (clp != curbp->b_linep) {
  509.         if (cbo == llength(clp)) {
  510.             clp = lforw(clp);
  511.             cbo = 0;
  512.             c = SEOL;
  513.         } else
  514.             c = lgetc(clp, cbo++);
  515.         if (eq(c, pat[0]) != FALSE) {
  516.             tlp = clp;
  517.             tbo = cbo;
  518.             pp  = &pat[1];
  519.             while (*pp != 0) {
  520.                 if (tlp == curbp->b_linep)
  521.                     goto fail;
  522.                 if (tbo == llength(tlp)) {
  523.                     tlp = lforw(tlp);
  524.                     if (tlp == curbp->b_linep)
  525.                         goto fail;
  526.                     tbo = 0;
  527.                     c = SEOL;
  528.                 } else
  529.                     c = lgetc(tlp, tbo++);
  530.                 if (eq(c, *pp++) == FALSE)
  531.                     goto fail;
  532.             }
  533.             curwp->w_dotp  = tlp;
  534.             curwp->w_doto  = tbo;
  535.             curwp->w_flag |= WFMOVE;
  536.             return (TRUE);
  537.         }
  538.     fail:    ;
  539.     }
  540.     return (FALSE);
  541. }
  542.  
  543. /*
  544.  * This routine does the real work of a
  545.  * backward search. The pattern is sitting in the external
  546.  * variable "pat". If found, dot is updated, the window system
  547.  * is notified of the change, and TRUE is returned. If the
  548.  * string isn't found, FALSE is returned.
  549.  */
  550. backsrch() {
  551.     register LINE    *clp;
  552.     register int    cbo;
  553.     register LINE    *tlp;
  554.     register int    tbo;
  555.     register int    c;
  556.     register char    *epp;
  557.     char        *pp;
  558.  
  559.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  560.         ;
  561.     clp = curwp->w_dotp;
  562.     cbo = curwp->w_doto;
  563.     for (;;) {
  564.         if (cbo == 0) {
  565.             clp = lback(clp);
  566.             if (clp == curbp->b_linep)
  567.                 return (FALSE);
  568.             cbo = llength(clp)+1;
  569.         }
  570.         if (--cbo == llength(clp))
  571.             c = SEOL;
  572.         else
  573.             c = lgetc(clp,cbo);
  574.         if (eq(c, *epp) != FALSE) {
  575.             tlp = clp;
  576.             tbo = cbo;
  577.             pp  = epp;
  578.             while (pp != &pat[0]) {
  579.                 if (tbo == 0) {
  580.                     tlp = lback(tlp);
  581.                     if (tlp == curbp->b_linep)
  582.                         goto fail;
  583.                     tbo = llength(tlp)+1;
  584.                 }
  585.                 if (--tbo == llength(tlp))
  586.                     c = SEOL;
  587.                 else
  588.                     c = lgetc(tlp,tbo);
  589.                 if (eq(c, *--pp) == FALSE)
  590.                     goto fail;
  591.             }
  592.             curwp->w_dotp  = tlp;
  593.             curwp->w_doto  = tbo;
  594.             curwp->w_flag |= WFMOVE;
  595.             return (TRUE);
  596.         }
  597.     fail:    ;
  598.     }
  599.     /* NOTREACHED*/
  600. }
  601.  
  602. /*
  603.  * Compare two characters.
  604.  * The "bc" comes from the buffer.
  605.  * It has its case folded out. The
  606.  * "pc" is from the pattern.
  607.  */
  608. eq(bc, pc) {
  609.     register int    ibc;
  610.     register int    ipc;
  611.  
  612.     ibc = bc & 0xFF;
  613.     ipc = pc & 0xFF;
  614.     if (ISLOWER(ibc) != FALSE)
  615.         ibc = TOUPPER(ibc);
  616.     if (ISLOWER(ipc) != FALSE)
  617.         ipc = TOUPPER(ipc);
  618.     if (ibc == ipc)
  619.         return (TRUE);
  620.     return (FALSE);
  621. }
  622.  
  623. /*
  624.  * Read a pattern.
  625.  * Stash it in the external variable "pat". The "pat" is
  626.  * not updated if the user types in an empty line. If the user typed
  627.  * an empty line, and there is no old pattern, it is an error.
  628.  * Display the old pattern, in the style of Jeff Lomicka. There is
  629.  * some do-it-yourself control expansion.
  630.  */
  631. readpattern(prompt) char *prompt; {
  632.     register int    s;
  633.     char        tpat[NPAT];
  634.  
  635.     if (tpat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
  636.     else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);
  637.  
  638.     if (s == TRUE)                /* Specified        */
  639.         (VOID) strcpy(pat, tpat);
  640.     else if (s==FALSE && pat[0]!=0)        /* CR, but old one    */
  641.         s = TRUE;
  642.     return (s);
  643. }
  644.